package wrapper

import (
	"playbook/ginsvr/src/orm"
	"playbook/ginsvr/src/utils/stringutil"
	"strconv"
	"strings"
)

const (
	SELECT           string = "SELECT"
	UPDATE           string = "UPDATE"
	INSERT           string = "INSERT"
	DELETE           string = "DELETE"
	WRAPPER          string = "wrapper"
	AND              string = "and"
	OR               string = "or"
	SPACING          string = " "
	LEFT_BRACKETS           = "("
	RIGHT_BRACKETS          = ")"
	QUOTES           string = "\""
	COMMA            string = ","
	FIXED            string = "0"
	UN_FIXED         string = "1"
	DOT              string = "."
	REQ_DTO          string = "reqDTO"
	GET              string = "get"
	IS               string = "is"
	SIMICOLON        string = ";"
	LINE_SEPRATOR    string = "\r\n"
	LAMDA_SIMBOL     string = "->"
	LEFT_BRACE       string = "{"
	RIGHT_BRACE      string = "}"
	FOUR_SPACING     string = "    "
	DOUBLE_QUOTATION string = "\""
)

type WapperParam struct {
	// SELECT\UPDATE\DELETE\INSERT
	OperationType string
	// 条件数组
	Conditions []Condition
}

type Condition struct {
	ConcatSymbol   string
	Keyword        string
	ColumnName     string
	ValueType      string
	ValueExpresion []string
	Children       []Condition
}

/**
 *构建Wrapper
**/
func BuildWrapper(wrapperParam WapperParam, atomSvrDefine orm.AtomSvrDefine, tableName string) string {
	var conditions = wrapperParam.Conditions
	var sb strings.Builder
	if isEmptyConditions(conditions) {
		return sb.String()
	}
	fieldsMap := GetFieldMap(tableName)
	var wrapperIndex int = 0
	var p *int = &wrapperIndex
	var spaceIndex int = 2
	var spIndex *int = &spaceIndex

	for i := 0; i < len(conditions); i++ {
		condition := conditions[i]
		sb.WriteString(buildNode(condition, p, spIndex, atomSvrDefine, fieldsMap))
	}

	return sb.String()[8:]
}

func buildNode(condition Condition, p *int, spaceIndex *int, atomSvrDefine orm.AtomSvrDefine, fieldsMap map[string]string) string {
	if strings.EqualFold(AND, condition.ConcatSymbol) {
		return buildAndCondition(condition, p, spaceIndex, atomSvrDefine, fieldsMap)
	} else {
		return buildOrCondition(condition, p, spaceIndex, atomSvrDefine, fieldsMap)
	}
}

func buildAndCondition(condition Condition, p *int, spaceIndex *int, atomSvrDefine orm.AtomSvrDefine, fieldsMap map[string]string) string {
	var sb strings.Builder
	if isEmptyConditions(condition.Children) {
		// 无子节点
		sb.WriteString(getSpaceStr(*spaceIndex))
		sb.WriteString(getWrapperName(*p))
		sb.WriteString(DOT)
		sb.WriteString(condition.Keyword)
		sb.WriteString(LEFT_BRACKETS)
		sb.WriteString(QUOTES)
		sb.WriteString(condition.ColumnName)
		sb.WriteString(QUOTES)
		sb.WriteString(COMMA)
		sb.WriteString(SPACING)
		//sb.WriteString(DOUBLE_QUOTATION)
		sb.WriteString(getValue(condition, atomSvrDefine, fieldsMap))
		//sb.WriteString(DOUBLE_QUOTATION)
		sb.WriteString(RIGHT_BRACKETS)
		sb.WriteString(SIMICOLON)
		sb.WriteString(LINE_SEPRATOR)
	} else {
		// 有子节点
		sb.WriteString(getSpaceStr(*spaceIndex))
		sb.WriteString(getWrapperName(*p))
		sb.WriteString(DOT)
		sb.WriteString(AND)
		sb.WriteString(LEFT_BRACKETS)
		*p++
		sb.WriteString(getWrapperName(*p))
		sb.WriteString(SPACING)
		sb.WriteString(LAMDA_SIMBOL)
		sb.WriteString(SPACING)
		sb.WriteString(LEFT_BRACE)
		*spaceIndex++
		sb.WriteString(LINE_SEPRATOR)
		for i := 0; i < len(condition.Children); i++ {
			sb.WriteString(buildNode(condition.Children[i], p, spaceIndex, atomSvrDefine, fieldsMap))
		}
		sb.WriteString(getSpaceStr(*spaceIndex - 1))
		sb.WriteString(RIGHT_BRACE)
		sb.WriteString(RIGHT_BRACKETS)
		sb.WriteString(SIMICOLON)
		sb.WriteString(LINE_SEPRATOR)

	}
	return sb.String()
}

func getSpaceStr(num int) string {
	var sb strings.Builder
	for i := 0; i < num; i++ {
		sb.WriteString(FOUR_SPACING)
	}
	return sb.String()
}

func getWrapperName(num int) string {
	if num == 0 {
		return WRAPPER
	}
	return WRAPPER + strconv.Itoa(num)
}

func buildOrCondition(condition Condition, p *int, spaceIndex *int, atomSvrDefine orm.AtomSvrDefine, fieldsMap map[string]string) string {
	var sb strings.Builder
	if isEmptyConditions(condition.Children) {
		// 无子节点
		sb.WriteString(getSpaceStr(*spaceIndex))
		sb.WriteString(getWrapperName(*p))
		sb.WriteString(DOT)
		sb.WriteString(OR)
		sb.WriteString(LEFT_BRACKETS)
		sb.WriteString(RIGHT_BRACKETS)
		sb.WriteString(DOT)
		sb.WriteString(condition.Keyword)
		sb.WriteString(LEFT_BRACKETS)
		sb.WriteString(QUOTES)
		sb.WriteString(condition.ColumnName)
		sb.WriteString(QUOTES)
		sb.WriteString(COMMA)
		sb.WriteString(SPACING)
		//sb.WriteString(DOUBLE_QUOTATION)
		sb.WriteString(getValue(condition, atomSvrDefine, fieldsMap))
		//sb.WriteString(DOUBLE_QUOTATION)
		sb.WriteString(RIGHT_BRACKETS)
		sb.WriteString(SIMICOLON)
		sb.WriteString(LINE_SEPRATOR)
	} else {
		// 有子节点
		sb.WriteString(getSpaceStr(*spaceIndex))
		sb.WriteString(getWrapperName(*p))
		sb.WriteString(DOT)
		sb.WriteString(OR)
		sb.WriteString(LEFT_BRACKETS)
		*p++
		sb.WriteString(getWrapperName(*p))
		sb.WriteString(SPACING)
		sb.WriteString(LAMDA_SIMBOL)
		sb.WriteString(SPACING)
		sb.WriteString(LEFT_BRACE)
		*spaceIndex++
		sb.WriteString(LINE_SEPRATOR)
		for i := 0; i < len(condition.Children); i++ {
			sb.WriteString(buildNode(condition.Children[i], p, spaceIndex, atomSvrDefine, fieldsMap))
		}
		sb.WriteString(getSpaceStr(*spaceIndex - 1))
		sb.WriteString(RIGHT_BRACE)
		sb.WriteString(RIGHT_BRACKETS)
		sb.WriteString(SIMICOLON)
		sb.WriteString(LINE_SEPRATOR)
	}
	return sb.String()
}

func isEmptyConditions(arr []Condition) bool {
	if arr == nil || len(arr) == 0 {
		return true
	}
	return false
}

func getValue(condition Condition, atomSvrDefine orm.AtomSvrDefine, fieldsMap map[string]string) string {
	var value string
	if FIXED == condition.ValueType {
		// 固定值
		if "String" == fieldsMap[condition.ColumnName] {
			value = DOUBLE_QUOTATION + condition.ValueExpresion[0] + DOUBLE_QUOTATION
		} else {
			value = condition.ValueExpresion[0]
		}
	} else {
		// 变量 Eg: InnerReqDTO.data.name
		value = ConvertVlaueExpresion(condition.ValueExpresion, atomSvrDefine)
	}
	return value
}

func ConvertVlaueExpresion(valueExpresion []string, atomSvrDefine orm.AtomSvrDefine) string {
	var sb strings.Builder
	sb.WriteString(REQ_DTO)
	sb.WriteString(".getData()")
	for i := 0; i < len(valueExpresion); i++ {
		sb.WriteString(DOT)
		if i == len(valueExpresion)-1 && isBoolValue(valueExpresion[i], i+1, atomSvrDefine, valueExpresion) {
			sb.WriteString(IS + stringutil.GetTitleStr(valueExpresion[i]))
		} else {
			sb.WriteString(GET + stringutil.GetTitleStr(valueExpresion[i]))
		}
		sb.WriteString(LEFT_BRACKETS)
		sb.WriteString(RIGHT_BRACKETS)
	}
	return sb.String()
}

func isBoolValue(propertyName string, deepLen int, atomSvrDefine orm.AtomSvrDefine, properties []string) bool {
	inputParam := orm.GetParamById(atomSvrDefine.Inputparamid)
	var paramRelation orm.Paramrelation
	var subParam orm.Parameter
	var parentId string = inputParam.Parameterid
	for i := 0; i < deepLen; i++ {
		paramRelation = orm.GetSubParameterbyName(parentId, properties[i])
		subParam = orm.GetParamById(paramRelation.Subparamid)
		parentId = subParam.Parameterid
	}
	return "boolean" == strings.ToLower(subParam.Type)
}

func GetFieldMap(tableName string) map[string]string {
	var fieldsMap map[string]string = make(map[string]string)
	fields := orm.GetTableAllFields(tableName)
	for _, field := range fields {
		fieldsMap[field.FieldName] = DbTypeToJvavaType(field.FieldType)
	}
	return fieldsMap
}

func DbTypeToJvavaType(dbType string) string {
	var javaType string
	if strings.HasPrefix(dbType, "varchar") {
		javaType = "String"
	} else if strings.HasPrefix(dbType, "timestamp") {
		javaType = "LocalDateTime"
	} else if strings.HasPrefix(dbType, "date") {
		javaType = "LocalDate"
	} else if strings.HasPrefix(dbType, "char") {
		javaType = "String"
	} else if strings.HasPrefix(dbType, "text") {
		javaType = "String"
	} else if strings.HasPrefix(dbType, "int2") {
		javaType = "Integer"
	} else if strings.HasPrefix(dbType, "int4") {
		javaType = "Integer"
	} else if strings.HasPrefix(dbType, "int8") {
		javaType = "Long"
	} else if strings.HasPrefix(dbType, "float4") {
		javaType = "Float"
	} else if strings.HasPrefix(dbType, "float8") {
		javaType = "Double"
	} else if strings.HasPrefix(dbType, "money") {
		javaType = "Double"
	} else if strings.HasPrefix(dbType, "numeric") {
		javaType = "BigDecimal"
	} else if strings.HasPrefix(dbType, "bit") {
		javaType = "Boolean"
	} else if strings.HasPrefix(dbType, "bool") {
		javaType = "Boolean"
	} else {
		javaType = dbType
	}
	return javaType
}
